package org.ihelpuoo.xml;

import org.ihelpuoo.lang.StringUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;

public class XMLUtils
{
	public static boolean equals(Node a, Node b)
	{
		return a.isEqualNode(b);
	}

	public static boolean equals(NodeList aList, NodeList bList)
	{
		if (null == aList || null == bList)
			return false;
		if (aList.getLength() != bList.getLength())
			return false;
		for (int i = 0; i < aList.getLength(); i++)
		{
			if (!equals(aList.item(i),bList.item(i)))
				return false;
		}
		return true;
	}

	public static boolean equalsIgnoreBlank(Document a, Document b)
	{
		Element rootA = a.getDocumentElement();
		Element rootB = b.getDocumentElement();
		return equalsIgnoreBlank(rootA,rootB);
	}

	public static boolean equalsIgnoreBlank(Node a, Node b)
	{
		if (null == a || null == b)
			return false;
		if (a.getNodeType() != b.getNodeType())
			return false;
		if (isElement(a))
		{
			if (!a.getNodeName().equals(b.getNodeName()))
				return false;
			NamedNodeMap attsA = a.getAttributes();
			NamedNodeMap attsB = b.getAttributes();
			if (attsA.getLength() != attsB.getLength())
				return false;
			for (int i = 0; i < attsA.getLength(); i++)
			{
				// @ TODO ZZH: if the attribute size is too long to more than 50,
				// maybe better to use a HashMap to index the name 
				Attr aa = (Attr)attsA.item(i);
				Attr ab = (Attr)attsB.getNamedItem(aa.getName());
				if (null == ab)
					return false;
				if (!aa.getValue().equals(ab.getValue()))
					return false;
			}
			return equalsIgnoreBlank(a.getChildNodes(),b.getChildNodes());
		}
		else if (isText(a) || isComment(a))
		{
			String[] saa = StringUtils.trim(a.getNodeValue()).split("\n");
			String[] sba = StringUtils.trim(b.getNodeValue()).split("\n");
			if (saa.length != sba.length)
				return false;
			for (int i = 0; i < saa.length; i++)
			{
				if (!StringUtils.trim(saa[i]).equals(StringUtils.trim(sba[i])))
					return false;
			}
			return true;
		}
		return a.isEqualNode(b);
	}

	public static boolean equalsIgnoreBlank(NodeList aList, NodeList bList)
	{
		int iA = 0;
		int iB = 0;
		for (; iA < aList.getLength(); iA++)
		{
			Node a = aList.item(iA);
			if (isText(a) && StringUtils.isBlank(a.getNodeValue()))
				continue;
			Node b = bList.item(iB);
			if (isText(b) && StringUtils.isBlank(b.getNodeValue()))
			{
				if (iB >= bList.getLength())
					return false;
				for (; iB < bList.getLength(); iB++)
				{
					b = bList.item(iB);
					if (!isText(b) || !StringUtils.isBlank(b.getNodeValue()))
						break;
				}
				if (isText(b) && StringUtils.isBlank(b.getNodeValue()))
					return false;
			}
			if (!equalsIgnoreBlank(a,b))
				return false;
			iB++;
		}
		for (; iB < bList.getLength(); iB++)
		{
			Node b = bList.item(iB);
			if (!isText(b) || !StringUtils.isBlank(b.getNodeValue()))
				return false;
		}
		return true;
	}

	public static boolean isElement(Node node)
	{
		if (null == node)
			return false;
		return Node.ELEMENT_NODE == node.getNodeType();
	}

	public static boolean isAttribute(Node node)
	{
		if (null == node)
			return false;
		return Node.ATTRIBUTE_NODE == node.getNodeType();
	}

	public static boolean isText(Node node)
	{
		if (null == node)
			return false;
		return Node.TEXT_NODE == node.getNodeType();
	}

	public static boolean isCDATA(Node node)
	{
		if (null == node)
			return false;
		return Node.CDATA_SECTION_NODE == node.getNodeType();
	}

	public static boolean isEntityReference(Node node)
	{
		if (null == node)
			return false;
		return Node.ENTITY_REFERENCE_NODE == node.getNodeType();
	}

	public static boolean isEntity(Node node)
	{
		if (null == node)
			return false;
		return Node.ENTITY_NODE == node.getNodeType();
	}

	public static boolean isProcessing(Node node)
	{
		if (null == node)
			return false;
		return Node.PROCESSING_INSTRUCTION_NODE == node.getNodeType();
	}

	public static boolean isComment(Node node)
	{
		if (null == node)
			return false;
		return Node.COMMENT_NODE == node.getNodeType();
	}

	public static boolean isDocument(Node node)
	{
		if (null == node)
			return false;
		return Node.DOCUMENT_NODE == node.getNodeType();
	}

	public static boolean isDocumentType(Node node)
	{
		if (null == node)
			return false;
		return Node.DOCUMENT_TYPE_NODE == node.getNodeType();
	}

	public static boolean isDocumentFragment(Node node)
	{
		if (null == node)
			return false;
		return Node.DOCUMENT_FRAGMENT_NODE == node.getNodeType();
	}

	public static boolean isNotation(Node node)
	{
		if (null == node)
			return false;
		return Node.NOTATION_NODE == node.getNodeType();
	}

	public static String escapeXmlTextContent(String str)
	{
		if (null == str)
			return null;
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < str.length(); i++)
		{
			char c = str.charAt(i);
			if (c == '>')
				sb.append("&gt;");
			else if (c == '<')
				sb.append("&lt;");
			else if (c == '&')
				sb.append("&amp;");
			else
				sb.append(c);
		}
		return sb.toString();
	}

	public static String escapeXmlAttributeValue(String str)
	{
		if (null == str)
			return null;
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < str.length(); i++)
		{
			char c = str.charAt(i);
			if (c == '<')
				sb.append("&lt;");
			else if (c == '&')
				sb.append("&amp;");
			else if (c == '"')
				sb.append("&quot;");
			else
				sb.append(c);
		}
		return sb.toString();
	}

	public static int removeAllChildren(Node node)
	{
		NodeList nl = node.getChildNodes();
		int re = nl.getLength();
		while (nl.getLength() > 0)
			node.removeChild(nl.item(0));
		return re;
	}

	public static Node dupicate(Node node, Document doc)
	{
		Node nn = null;
		if (XMLUtils.isElement(node))
		{
			nn = doc.createElement(((Element)node).getTagName());
			NodeList nl = node.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++)
				nn.appendChild(dupicate(nl.item(i),doc));
		}
		else if (XMLUtils.isComment(node))
		{
			nn = doc.createComment(node.getTextContent());
		}
		else if (XMLUtils.isText(node))
		{
			nn = doc.createTextNode(node.getTextContent());
		}
		else if (XMLUtils.isCDATA(node))
		{
			nn = doc.createCDATASection(node.getTextContent());
		}
		else if (XMLUtils.isProcessing(node))
		{
			nn = doc.createProcessingInstruction(((ProcessingInstruction)node).getTarget(),node
					.getNodeValue());
		}
		else if (XMLUtils.isEntityReference(node))
		{
			nn = doc.createEntityReference(node.getNodeValue());
		}
		return nn;
	}
}
